﻿using System;
using FarseerGames.FarseerPhysics.Collisions;
using FarseerGames.FarseerPhysics.Dynamics;
using FarseerGames.FarseerPhysics.Factories;
using FarseerGames.FarseerPhysics.Mathematics;
using FreeGoo.GameEngine.Renderables;
using FreeGoo.GameEngine.Renderables.BodyHookOwners;
using FreeGoo.GameEngine.Renderables.Links;

namespace FreeGoo.GameEngine
{
    // TODO: Refactor this factory so that it becomes multiple factories instead
    public static class MixedFactory
    {
        // The Magic Numbers!
        public static readonly float Density = 1;
        public static readonly float PinJointBreakpoint = 0.25f;
        public static readonly Vector2 Gravity = new Vector2(0, 500.0f);
        public static readonly int DefaultCircleEdges = 20;
        public static readonly float DefaultPinJointSoftness = 0.0045f;
        private static float _pinJointSoftness = 0.0045f;
        public static float PinJointSoftness { get { return _pinJointSoftness; } set { _pinJointSoftness = value; } }

        public static BodyRenderable CreateRectangle(Engine engine, Vector2 position, Vector2 size, float density, float angle)
        {
            BodyRenderable rectangle = InternalCreateRectangle(engine, position, size, density, angle);
            engine.Add(rectangle);
            return rectangle;
        }

        public static BodyRenderable CreateFixedRectangle(Engine engine, Vector2 position, Vector2 size, float angle)
        {
            BodyRenderable fixedRectangle = InternalCreateRectangle(engine, position, size, 1, angle);
            fixedRectangle.Body.IsStatic = true;
            engine.Add(fixedRectangle);
            return fixedRectangle;
        }

        public static BodyRenderable CreateCircle(Engine engine, Vector2 position, float radius, float density, float angle, int edges)
        {
            Body body = BodyFactory.Instance.CreateCircleBody(radius, (float)(density * Math.PI * radius * radius));
            body.Position = position;
            Geom geom = GeomFactory.Instance.CreateCircleGeom(body, radius, edges);
            body.Rotation = angle;
            BodyRenderable bodyRenderable = new BodyRenderable(
                geom);
            engine.Add(bodyRenderable);
            return bodyRenderable;
        }

        public static Link CreatePinJointLinkBetweenLinkableObjects(Engine engine, ILinkable first, ILinkable second, ILinkable owningLinkable)
        {
            Link link = new PinJointLink(first, second, owningLinkable);
            link.Joint.Softness = PinJointSoftness;
            link.Joint.Breakpoint = PinJointBreakpoint * link.TargetDistance;
            engine.Add(link);
            return link;
        }

        public static Link CreateSliderJointLinkBetweenLinkableObjects(Engine engine, ILinkable first, ILinkable second, ILinkable owningLinkable)
        {
            Link link = new SliderJointLink(first, second, owningLinkable);
            link.Joint.Softness = PinJointSoftness;
            link.Joint.Breakpoint = PinJointBreakpoint * link.TargetDistance;
            engine.Add(link);
            return link;
        }

        public static Link CreateStringLinkBetweenLinkableObjects(Engine engine, ILinkable first, ILinkable second, ILinkable owningLinkable)
        {
            Link link = new StringLink(first, second, owningLinkable);
            link.Joint.Softness = PinJointSoftness;
            link.Joint.Breakpoint = PinJointBreakpoint * link.TargetDistance;
            engine.Add(link);
            return link;
        }

        public static Goal CreateGoal(Engine engine, Vector2 position, string acceptsGooTypes)
        {
            Goal goal = new Goal(position, acceptsGooTypes);
            engine.Add(goal);
            return goal;
        }

        public static BodyHook CreateBodyHook(Engine engine, BodyRenderable owningBodyRenderable, Vector2 worldPosition, float radius)
        {
            BodyHook bodyHook = new BodyHook(owningBodyRenderable, worldPosition, radius);
            engine.Add(bodyHook);
            return bodyHook;
        }

        public static Motor CreateMotor(Engine engine, Vector2 worldPosition, float hookRadius, int hookCount)
        {
            Body body = BodyFactory.Instance.CreateCircleBody(hookRadius, (float)(Density * Math.PI * hookRadius * hookRadius));
            body.Position = worldPosition;
            Geom geom = GeomFactory.Instance.CreateCircleGeom(body, hookRadius, hookCount);

            Motor motor = new Motor(geom, worldPosition, hookRadius, hookCount);
            engine.Add(motor);
            return motor;
        }

        public static KeyedMotor CreateKeyedMotor(Engine engine, Vector2 worldPosition, float hookRadius, int hookCount)
        {
            Body body = BodyFactory.Instance.CreateCircleBody(hookRadius, (float)(Density * Math.PI * hookRadius * hookRadius));
            body.Position = worldPosition;
            Geom geom = GeomFactory.Instance.CreateCircleGeom(body, hookRadius, hookCount);

            KeyedMotor motor = new KeyedMotor(geom, worldPosition, hookRadius, hookCount);
            engine.Add(motor);
            return motor;
        }

        internal static BodyRenderable InternalCreateRectangle(Engine engine, Vector2 position, Vector2 size, float density, float angle)
        {
            Body body = BodyFactory.Instance.CreateRectangleBody(size.X, size.Y, size.X * size.Y * density);
            body.Position = position;
            Geom geom = GeomFactory.Instance.CreateRectangleGeom(body, size.X, size.Y);
            body.Rotation = angle;
            BodyRenderable bodyRenderable = new BodyRenderable(
                geom);
            return bodyRenderable;
        }
    }
}
